package generator

import (
	"bytes"
	"fmt"
	"go/ast"
	"go/format"
	"go/types"
	"log"
	"os"
	"path"
	"sort"
	"strings"

	"golang.org/x/tools/go/packages"

	"springboot/framework/meta"
)

// makeProxyName 保证类名只有一个 Proxy 后缀
func makeProxyName(typeName string) string {
	if strings.HasSuffix(typeName, "Proxy") {
		return typeName
	}
	return typeName + "Proxy"
}

// buildDependencyGraph 构建依赖关系图
func buildDependencyGraph(beans []meta.BeanDefinition) map[string][]string {
	depGraph := make(map[string][]string)
	for _, b := range beans {
		for _, f := range b.AutoWiredFields {
			depGraph[b.BeanName] = append(depGraph[b.BeanName], f.BeanName)
		}
	}
	return depGraph
}

// topoSortBeanDefinitions 对 BeanDefinition 按依赖排序
func topoSortBeanDefinitions(beans []meta.BeanDefinition) []meta.BeanDefinition {
	depGraph := buildDependencyGraph(beans)

	beanMap := make(map[string]meta.BeanDefinition)
	for _, b := range beans {
		beanMap[b.BeanName] = b
	}

	visited := make(map[string]bool)
	temp := make(map[string]bool)
	result := []meta.BeanDefinition{}

	var visit func(name string)
	visit = func(name string) {
		if visited[name] {
			return
		}
		if temp[name] {
			panic("循环依赖: " + name)
		}
		temp[name] = true
		for _, dep := range depGraph[name] {
			visit(dep)
		}
		temp[name] = false
		visited[name] = true
		if bean, ok := beanMap[name]; ok {
			result = append(result, bean)
		}
	}

	for _, b := range beans {
		visit(b.BeanName)
	}

	return result
}

// ===== 新增：Graphviz 依赖图导出 =====
func exportGraphviz(beans []meta.BeanDefinition, aspects []meta.AspectDefinition, depGraph map[string][]string) {
	var buf strings.Builder
	buf.WriteString("digraph BeanGraph {\n")
	buf.WriteString("    rankdir=LR;\n")
	buf.WriteString("    node [shape=box, style=filled, fontname=\"Microsoft Yahei\"];\n\n")

	// 记录切面 Bean
	aspectMap := make(map[string]bool)
	for _, asp := range aspects {
		aspectMap[asp.BeanName] = true
	}

	// 节点
	for _, b := range beans {
		if aspectMap[b.BeanName] {
			buf.WriteString(fmt.Sprintf("    \"%s\" [color=red, fillcolor=mistyrose];\n", b.BeanName))
		} else {
			buf.WriteString(fmt.Sprintf("    \"%s\" [color=blue, fillcolor=lightcyan];\n", b.BeanName))
		}
	}

	buf.WriteString("\n")
	// 依赖关系（b -> dep）
	for src, deps := range depGraph {
		for _, dep := range deps {
			buf.WriteString(fmt.Sprintf("    \"%s\" -> \"%s\";\n", src, dep))
		}
	}

	buf.WriteString("}\n")

	_ = os.WriteFile("bean_graph.dot", []byte(buf.String()), 0644)
	fmt.Println("📄 依赖关系图已生成: bean_graph.dot（使用 Graphviz 打开可视化）")
}

func CodeGen() {
	beans, aspects, err := meta.BeanMetaScans("./...")
	if err != nil {
		log.Fatal(err)
	}

	// 打印扫描结果（调试用）
	for _, bean := range beans {
		fmt.Println(bean)
	}
	for _, aspect := range aspects {
		fmt.Println(aspect)
	}

	// 过滤掉 spring_gen 生成目录的 bean
	filteredBeans := make([]meta.BeanDefinition, 0)
	for _, b := range beans {
		if strings.Contains(b.ImportPath, "spring_gen") {
			continue
		}
		filteredBeans = append(filteredBeans, b)
	}
	// 依赖图
	depGraph := buildDependencyGraph(filteredBeans)

	// ✅ 导出 Graphviz (可选步骤)
	exportGraphviz(filteredBeans, aspects, depGraph)
	// ✅ 先拓扑排序
	beans = topoSortBeanDefinitions(filteredBeans)

	var buf bytes.Buffer
	buf.WriteString("// Code generated by gen_container.go; DO NOT EDIT.\n")
	buf.WriteString("package spring_gen\n\n")

	needStrconv, needJSON := false, false
	imports := map[string]struct{}{}
	imports["springboot/framework/container"] = struct{}{}

	// 自动收集 import
	for _, b := range beans {
		imports[b.ImportPath] = struct{}{}
		for _, cf := range b.ConfigFields {
			if strings.HasPrefix(cf.GoType, "[]") {
				needJSON = true
			} else {
				switch cf.GoType {
				case "int", "int8", "int16", "int32", "int64",
					"uint", "uint8", "uint16", "uint32", "uint64",
					"float32", "float64", "bool":
					needStrconv = true
				}
			}
		}
		// ConfigurationProperties 情况
		if b.ConfigPrefix != "" {
			fields := getStructFields(b.ImportPath, b.TypeName)
			for _, f := range fields {
				if strings.HasPrefix(f.GoType, "[]") {
					needJSON = true
				} else {
					switch f.GoType {
					case "int", "int8", "int16", "int32", "int64",
						"uint", "uint8", "uint16", "uint32", "uint64",
						"float32", "float64", "bool":
						needStrconv = true
					}
				}
			}
		}

		for _, imp := range b.Implements {
			ifacePkg := imp[:strings.LastIndex(imp, ".")]
			if ifacePkg != "" {
				imports[ifacePkg] = struct{}{}
			}
		}
	}

	// 切面Bean包导入
	for _, asp := range aspects {
		for _, b := range beans {
			if b.BeanName == asp.BeanName {
				imports[b.ImportPath] = struct{}{}
			}
		}
	}

	// imports
	buf.WriteString("import (\n")
	if needStrconv {
		buf.WriteString(`    "strconv"` + "\n")
	}
	if needJSON {
		buf.WriteString(`    "encoding/json"` + "\n")
	}
	for imp := range imports {
		buf.WriteString(fmt.Sprintf("    \"%s\"\n", imp))
	}
	buf.WriteString(")\n\n")

	// 生成代理类
	for _, b := range beans {
		for _, iface := range b.Implements {
			asps := aspectsForTarget(aspects, iface) // 已排序
			if len(asps) > 0 {
				ifacePkgAlias := path.Base(iface[:strings.LastIndex(iface, ".")])
				proxyName := makeProxyName(b.TypeName)
				buf.WriteString(generateProxyStruct(iface, proxyName, ifacePkgAlias))
			}
		}
	}

	// InitContainer
	buf.WriteString("func InitContainer(config map[string]string) *container.Container {\n")
	buf.WriteString("\tc := container.New(config)\n\n")

	// 注册切面 Bean（固定优先）
	for _, asp := range aspects {
		pkgAlias := findPkgAlias(beans, asp.BeanName)
		typeName := findTypeName(beans, asp.BeanName)
		buf.WriteString(fmt.Sprintf("\tbean_%s := &%s.%s{}\n", asp.BeanName, pkgAlias, typeName))
		buf.WriteString(fmt.Sprintf("\tc.RegisterBean(\"%s\", (*container.Aspect)(nil), bean_%s)\n\n", asp.BeanName, asp.BeanName))
	}

	// 注册普通 Bean（已经拓扑排序）
	for _, b := range beans {
		if isAspectBean(b.BeanName, aspects) {
			continue
		}

		beanVar := "bean_" + b.BeanName
		asps := []meta.AspectDefinition{}
		for _, iface := range b.Implements {
			asps = append(asps, aspectsForTarget(aspects, iface)...)
		}

		isProxied := len(asps) > 0
		if isProxied {
			aspectArgs := []string{}
			for _, asp := range asps {
				aspectArgs = append(aspectArgs, fmt.Sprintf("c.GetBeanByName(\"%s\").(container.Aspect)", asp.BeanName))
			}
			buf.WriteString(fmt.Sprintf("\t%s := &%s{target: &%s.%s{}, aspects: []container.Aspect{%s}}\n",
				beanVar, makeProxyName(b.TypeName), path.Base(b.ImportPath), b.TypeName, strings.Join(aspectArgs, ", ")))
		} else {
			buf.WriteString(fmt.Sprintf("\t%s := &%s.%s{}\n", beanVar, path.Base(b.ImportPath), b.TypeName))
		}

		// 配置注入（普通）
		targetExpr := beanVar
		if isProxied {
			targetExpr = fmt.Sprintf("%s.target.(*%s.%s)", beanVar, path.Base(b.ImportPath), b.TypeName)
		}
		for _, cf := range b.ConfigFields {
			buf.WriteString(generateConfigInjectCode(targetExpr, cf.FieldName, cf.ConfigKey, cf.GoType))
		}

		// 配置注入（ConfigurationProperties）
		if b.ConfigPrefix != "" {
			fields := getStructFields(b.ImportPath, b.TypeName)
			for _, f := range fields {
				key := b.ConfigPrefix + "." + strings.ToLower(f.Name[:1]) + f.Name[1:]
				buf.WriteString(generateConfigInjectCode(targetExpr, f.Name, key, f.GoType))
			}
		}

		// Autowired
		for _, f := range b.AutoWiredFields {
			buf.WriteString(fmt.Sprintf("\t%s.%s = c.GetBeanByName(\"%s\").(%s)\n", targetExpr, f.FieldName, f.BeanName, f.TypeName))
		}

		// 注册 Bean
		if len(b.Implements) > 0 {
			iface := b.Implements[0]
			lastDot := strings.LastIndex(iface, ".")
			pkgAlias := path.Base(iface[:lastDot])
			ifaceName := iface[lastDot+1:]
			buf.WriteString(fmt.Sprintf("\tc.RegisterBean(\"%s\", (*%s.%s)(nil), %s)\n\n", b.BeanName, pkgAlias, ifaceName, beanVar))
		} else {
			buf.WriteString(fmt.Sprintf("\tc.RegisterBean(\"%s\", nil, %s)\n\n", b.BeanName, beanVar))
		}
	}

	buf.WriteString("\treturn c\n}\n")

	src, err := format.Source(buf.Bytes())
	if err != nil {
		log.Fatal("代码格式化失败:", err, "\n源码:\n", buf.String())
	}

	_ = os.MkdirAll("spring_gen", os.ModePerm)
	if err := os.WriteFile("spring_gen/container_gen.go", src, 0644); err != nil {
		log.Fatal("写文件失败:", err)
	}

	fmt.Println("✅ container_gen.go 生成成功（已按依赖顺序排序）")
}
func generateConfigInjectCode(targetExpr, fieldName, configKey, goType string) string {
	switch goType {
	case "string":
		return fmt.Sprintf("\t%s.%s = config[\"%s\"]\n", targetExpr, fieldName, configKey)
	case "int", "int8", "int16", "int32", "int64":
		return fmt.Sprintf("\tif iv, err := strconv.ParseInt(config[\"%s\"], 10, 64); err == nil {\n\t\t%s.%s = %s(iv)\n\t}\n", configKey, targetExpr, fieldName, goType)
	case "uint", "uint8", "uint16", "uint32", "uint64":
		return fmt.Sprintf("\tif uv, err := strconv.ParseUint(config[\"%s\"], 10, 64); err == nil {\n\t\t%s.%s = %s(uv)\n\t}\n", configKey, targetExpr, fieldName, goType)
	case "float32", "float64":
		return fmt.Sprintf("\tif fv, err := strconv.ParseFloat(config[\"%s\"], 64); err == nil {\n\t\t%s.%s = %s(fv)\n\t}\n", configKey, targetExpr, fieldName, goType)
	case "bool":
		return fmt.Sprintf("\tif bv, err := strconv.ParseBool(config[\"%s\"]); err == nil {\n\t\t%s.%s = bv\n\t}\n", configKey, targetExpr, fieldName)
	default:
		if strings.HasPrefix(goType, "[]") {
			return fmt.Sprintf("\tif s := config[\"%s\"]; s != \"\" {\n\t\t_ = json.Unmarshal([]byte(s), &%s.%s)\n\t}\n", configKey, targetExpr, fieldName)
		}
	}
	return ""
}

func getStructFields(importPath, typeName string) []struct {
	Name   string
	GoType string
} {
	cfg := &packages.Config{Mode: packages.NeedSyntax | packages.NeedTypes | packages.NeedTypesInfo}
	pkgs, err := packages.Load(cfg, importPath)
	if err != nil || len(pkgs) == 0 {
		return nil
	}

	var fields []struct {
		Name   string
		GoType string
	}
	for _, file := range pkgs[0].Syntax {
		for _, decl := range file.Decls {
			if gd, ok := decl.(*ast.GenDecl); ok {
				for _, spec := range gd.Specs {
					if ts, ok := spec.(*ast.TypeSpec); ok && ts.Name.Name == typeName {
						if st, ok := ts.Type.(*ast.StructType); ok {
							for _, f := range st.Fields.List {
								if len(f.Names) == 0 {
									continue
								}
								fname := f.Names[0].Name
								ftype := types.TypeString(pkgs[0].TypesInfo.TypeOf(f.Type), func(p *types.Package) string {
									return p.Name()
								})
								fields = append(fields, struct {
									Name   string
									GoType string
								}{fname, ftype})
							}
						}
					}
				}
			}
		}
	}
	return fields
}

func isAspectBean(beanName string, aspects []meta.AspectDefinition) bool {
	for _, asp := range aspects {
		if asp.BeanName == beanName {
			return true
		}
	}
	return false
}

func findPkgAlias(beans []meta.BeanDefinition, beanName string) string {
	for _, b := range beans {
		if b.BeanName == beanName {
			return path.Base(b.ImportPath)
		}
	}
	return ""
}

func findTypeName(beans []meta.BeanDefinition, beanName string) string {
	for _, b := range beans {
		if b.BeanName == beanName {
			return b.TypeName
		}
	}
	return ""
}

// aspectsForTarget 加入排序
func aspectsForTarget(aspects []meta.AspectDefinition, iface string) []meta.AspectDefinition {
	//如果是代理接口，则直接返回
	if iface == "springboot/framework/container.Aspect" {
		return nil
	}
	var list []meta.AspectDefinition
	for _, asp := range aspects {
		if asp.TargetType == iface {
			list = append(list, asp)
		}
	}
	sort.Slice(list, func(i, j int) bool {
		return list[i].Order < list[j].Order
	})
	return list
}

// 自动生成代理类及方法，支持Around调用
func generateProxyStruct(ifaceFullName, proxyName, ifacePkgAlias string) string {
	//生成代理类
	fmt.Println("开始生成代理类...")
	lastDot := strings.LastIndex(ifaceFullName, ".")
	pkgPath := ifaceFullName[:lastDot]
	ifaceName := ifaceFullName[lastDot+1:]

	cfg := &packages.Config{Mode: packages.NeedTypes}
	pkgs, err := packages.Load(cfg, pkgPath)
	if err != nil || len(pkgs) == 0 {
		panic("加载接口包失败: " + err.Error())
	}

	var iface *types.Interface
	for _, name := range pkgs[0].Types.Scope().Names() {
		if name == ifaceName {
			if t, ok := pkgs[0].Types.Scope().Lookup(name).Type().Underlying().(*types.Interface); ok {
				iface = t
				break
			}
		}
	}
	if iface == nil {
		panic("未找到接口：" + ifaceFullName)
	}

	qualifier := func(p *types.Package) string {
		return path.Base(p.Path())
	}

	var buf strings.Builder
	buf.WriteString(fmt.Sprintf("type %s struct {\n", proxyName))
	buf.WriteString(fmt.Sprintf("    target %s.%s\n", ifacePkgAlias, ifaceName))
	buf.WriteString("    aspects []container.Aspect\n}\n\n")

	for i := 0; i < iface.NumMethods(); i++ {
		m := iface.Method(i)
		sig := m.Type().(*types.Signature)
		var paramDefs, callArgs []string
		for pi := 0; pi < sig.Params().Len(); pi++ {
			p := sig.Params().At(pi)
			pname := p.Name()
			if pname == "" {
				pname = fmt.Sprintf("arg%d", pi)
			}
			paramDefs = append(paramDefs, fmt.Sprintf("%s %s", pname, types.TypeString(p.Type(), qualifier)))
			callArgs = append(callArgs, pname)
		}
		var resDefs, resVars []string
		for ri := 0; ri < sig.Results().Len(); ri++ {
			r := sig.Results().At(ri)
			resDefs = append(resDefs, types.TypeString(r.Type(), qualifier))
			resVars = append(resVars, fmt.Sprintf("r%d", ri))
		}

		buf.WriteString(fmt.Sprintf("func (p *%s) %s(%s)", proxyName, m.Name(), strings.Join(paramDefs, ", ")))
		if len(resDefs) > 0 {
			buf.WriteString(fmt.Sprintf(" (%s)", strings.Join(resDefs, ", ")))
		}
		buf.WriteString(" {\n")

		buf.WriteString(fmt.Sprintf("    res, err := container.CallWithAround(p.aspects, 0, \"%s\", []interface{}{%s}, func() (interface{}, error) {\n",
			m.Name(), strings.Join(callArgs, ", ")))

		if len(resVars) > 0 {
			buf.WriteString(fmt.Sprintf("        %s := p.target.%s(%s)\n", strings.Join(resVars, ", "), m.Name(), strings.Join(callArgs, ", ")))
			if len(resVars) == 1 {
				buf.WriteString(fmt.Sprintf("        return %s, nil\n", resVars[0]))
			} else {
				buf.WriteString(fmt.Sprintf("        return struct{%s}{%s}, nil\n", strings.Join(resDefs, "; "), strings.Join(resVars, ", ")))
			}
		} else {
			buf.WriteString(fmt.Sprintf("        p.target.%s(%s)\n", m.Name(), strings.Join(callArgs, ", ")))
			buf.WriteString("        return nil, nil\n")
		}
		buf.WriteString("    })\n")

		if len(resVars) > 0 {
			if len(resVars) == 1 {
				buf.WriteString(fmt.Sprintf("    if err != nil { var zero %s; return zero }\n", resDefs[0]))
				buf.WriteString(fmt.Sprintf("    return res.(%s)\n", resDefs[0]))
			} else {
				buf.WriteString("    if err != nil {\n")
				for _, rd := range resDefs {
					buf.WriteString(fmt.Sprintf("        var zero%s %s\n", rd, rd))
				}
				zeronames := []string{}
				for _, rd := range resDefs {
					zeronames = append(zeronames, "zero"+rd)
				}
				buf.WriteString(fmt.Sprintf("        return %s\n", strings.Join(zeronames, ", ")))
				buf.WriteString("    }\n")
				buf.WriteString(fmt.Sprintf("    rs := res.(struct{%s})\n", strings.Join(resDefs, "; ")))
				extracts := []string{}
				for _, rd := range resDefs {
					extracts = append(extracts, "rs."+rd)
				}
				buf.WriteString(fmt.Sprintf("    return %s\n", strings.Join(extracts, ", ")))
			}
		} else {
			buf.WriteString("    _ = res // no return value\n")
		}
		buf.WriteString("}\n\n")
	}

	return buf.String()
}
